package cn.com.jonpad.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;

/**
 * 授权服务器配置适配器
 *
 * @author Jon Chan
 * @date 2018/9/10 14:57
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private DataSource dataSource;

	@Autowired
	private UserDetailsService userDetailsService;

	@Autowired
	private RedisConnectionFactory redisConnectionFactory;

	/**
	 * token存储Redis
	 * @return
	 */
	@Bean
	RedisTokenStore redisTokenStore(){
		return new RedisTokenStore(redisConnectionFactory);
	}

	//token存储数据库
//    @Bean
    JdbcTokenStore jdbcTokenStore(){
        return new JdbcTokenStore(dataSource);
    }

	/**
	 * 客户详细信息服务配置
	 * 客户端详情信息在这里进行初始化，你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信
	 * @param clients
	 * @throws Exception 异常
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.withClientDetails(clientDetails());
		//配置两个客户端,一个用于password认证一个用于client认证
		/*clients.inMemory().withClient("client_1")
				.resourceIds("order")
				.authorizedGrantTypes("client_credentials","password",  "refresh_token")
				.scopes("select")
				.authorities("client")
				.secret("123456")
				.and().withClient("client_2")
				.resourceIds("order")
				.authorizedGrantTypes("password", "refresh_token")
				.scopes("select")
				.authorities("client")
				.secret("123456");*/
	}
	@Bean
	public ClientDetailsService clientDetails() {
		return new JdbcClientDetailsService(dataSource);
	}

	/**
	 * 授权服务器端点配置器
	 * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)。
	 * @param endpoints 端点
	 * @throws Exception 异常
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		//设置TokenStore存储库 //不设置默认使用内存作为储存库
		endpoints.tokenStore(redisTokenStore())
				//.userDetailsService(userDetailsService)
				.authenticationManager(authenticationManager);
		endpoints.tokenServices(defaultTokenServices());
	}

	/**
	 * <p>注意，自定义TokenServices的时候，需要设置@Primary，否则报错，</p>
	 * @return DefaultTokenServices
	 */
	@Primary
	@Bean
	public DefaultTokenServices defaultTokenServices(){
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		tokenServices.setTokenStore(redisTokenStore());
		tokenServices.setSupportRefreshToken(true);
		tokenServices.setClientDetailsService(clientDetails());
		// token有效期自定义设置，默认12小时
		tokenServices.setAccessTokenValiditySeconds(60*60*12);
		// 默认30天，这里修改
		tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
		return tokenServices;
	}

	/**
	 * 授权服务器安全配置器
	 * 用来配置令牌端点(Token Endpoint)的安全约束.
	 * @param security
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security.tokenKeyAccess("permitAll()");
		security.checkTokenAccess("isAuthenticated()");
		security.allowFormAuthenticationForClients();
	}

}
